//
// Created by Administrator on 2018/11/21.
//
#include <SimplePlayer.h>

void SimplePlay::open(JavaVM *javaVM, jobject instance, const char *filePath,
                      ANativeWindow *pANativeWindow) {
    this->javaVM = javaVM;
    this->instance = instance;
    this->pANativeWindow = pANativeWindow;
    LOGE("filepath=%s",filePath);
    av_register_all();

    formatContext = avformat_alloc_context();
    if (avformat_open_input(&formatContext, filePath, NULL, NULL) != 0) {
        LOGE("open %s failed!", filePath);
        return;
    }
    if (avformat_find_stream_info(formatContext, NULL)) {
        LOGE("avformat_find_stream_info failed!");
        return;
    }

    videoStream = av_find_best_stream(formatContext,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);

    audioStream = av_find_best_stream(formatContext,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);

    if (audioStream == -1 || videoStream == -1) {
        LOGE("not find audioStream or videoStream!");
        return;
    }
    videoContext = formatContext->streams[videoStream]->codec;
    audioContext = formatContext->streams[audioStream]->codec;
    videoCodec = avcodec_find_decoder(videoContext->codec_id);
    audioCodec = avcodec_find_decoder(audioContext->codec_id);
    if (avcodec_open2(videoContext, videoCodec, NULL) < 0) {
        LOGE("can not open video stream!");
        return;
    }
    if (avcodec_open2(audioContext, audioCodec, NULL) < 0) {
        LOGE("can not open audio stream!");
        return;
    }
    thread readThread(&SimplePlay::read, this);
    thread decodeAudioThread(&SimplePlay::decodeAudio, this);
    thread decodeVideoThread(&SimplePlay::decodeVideo, this);
    thread playAudioThread(&SimplePlay::playAudio, this);
    thread playVideoThread(&SimplePlay::playVideo, this);
    readThread.join();
    decodeAudioThread.join();
    decodeVideoThread.join();
    playAudioThread.join();
    playVideoThread.join();
}



void SimplePlay::read() {

        while (true) {
            if(videoPktQueue.size()<MAX_PKT_SIZE&&audioPktQueue.size()<MAX_PKT_SIZE)
            {
                AVPacket *pkt = av_packet_alloc();
                if(av_read_frame(formatContext, pkt) >= 0){
                    if (pkt->stream_index == videoStream) {
                        unique_lock<mutex> lock(videoPktMutex);
                        videoPktQueue.push(pkt);
                    } else if (pkt->stream_index == audioStream) {
                        unique_lock<mutex> lock(audioPktMutex);
                        audioPktQueue.push(pkt);
                    }else{
                        av_packet_unref(pkt);//av_packet_unref减少数据域的引用技术，当引用技术减为0时，会自动释放数据域所占用的空间
                        av_packet_free(&pkt);
                    }
                }
            }
        }
}



AVPacket * SimplePlay::getVideoPacket() {

    while(true){
        unique_lock<mutex> lock(videoPktMutex);
        if(videoPktQueue.size()>0){
            AVPacket *avPacket = videoPktQueue.front();
            videoPktQueue.pop();
            return avPacket;
        }
    }
}

AVPacket * SimplePlay::getAudioPacket() {
    while(true){
        unique_lock<mutex> lock(audioPktMutex);
        if(audioPktQueue.size()>0){
            AVPacket *avPacket = audioPktQueue.front();
            audioPktQueue.pop();
            return avPacket;
        }
    }
}




void SimplePlay::decodeVideo() {

    while (true) {
        if (videoPktQueue.size() > 0&&videoFrameQueue.size()<MAX_FRAME_SIZE)
        {
            AVPacket *pkt = getVideoPacket();
            int ret = avcodec_send_packet(videoContext, pkt);
            while (ret >= 0) {
                AVFrame *avFrame = av_frame_alloc();
                ret = avcodec_receive_frame(videoContext, avFrame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    break;
                }
                unique_lock<mutex> lock(videoFrameMutex);
                videoFrameQueue.push(avFrame);
            }
            av_packet_free(&pkt);
        }
    }
}

void SimplePlay::decodeAudio() {
    while (true) {
        if (audioPktQueue.size() > 0&&audioFrameQueue.size()<MAX_FRAME_SIZE)
        {
            AVPacket *pkt = getAudioPacket();
            int ret = avcodec_send_packet(audioContext, pkt);
            while (ret >= 0) {
                AVFrame *avFrame = av_frame_alloc();
                ret = avcodec_receive_frame(audioContext, avFrame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    break;
                }
                unique_lock<mutex> lock(audioFrameMutex);
                audioFrameQueue.push(avFrame);
            }
            av_packet_free(&pkt);
        }
    }
}


AVFrame* SimplePlay::getVideoFrame() {
    while (true){
        unique_lock<mutex> lock(videoFrameMutex);
        if(videoFrameQueue.size()>0){
            AVFrame* frame = videoFrameQueue.front();
            videoFrameQueue.pop();
            return frame;
        }
    }
}

AVFrame* SimplePlay::getAudioFrame() {
    while (true){
        unique_lock<mutex> lock(audioFrameMutex);
        if(audioFrameQueue.size()>0){
            AVFrame* frame = audioFrameQueue.front();
            audioFrameQueue.pop();
            return frame;
        }
    }
}

void SimplePlay::playVideo() {

    ANativeWindow_setBuffersGeometry(pANativeWindow,videoContext->width,videoContext->height,WINDOW_FORMAT_RGBA_8888);//初始化窗口
    SwsContext * pSwsCtx = sws_getContext(videoContext->width,videoContext->height,videoContext->pix_fmt,
                                          videoContext->width,videoContext->height,AV_PIX_FMT_RGBA,
                                          SWS_BILINEAR,NULL,NULL,NULL);//初始化图形转化上下文
    int outSize=av_image_get_buffer_size(AV_PIX_FMT_RGBA,videoContext->width,videoContext->height,1);
    uint8_t * outBuffer = (uint8_t *) malloc(sizeof(uint8_t) * outSize);
//    AVFrame * pAVFrame=av_frame_alloc();
    AVFrame * rgbFrame=av_frame_alloc();
    av_image_fill_arrays(rgbFrame->data,rgbFrame->linesize,
                         outBuffer,AV_PIX_FMT_RGBA,videoContext->width,videoContext->height,1);//初始化图片帧
    ANativeWindow_Buffer windowBuffer;
    while (true) {
        if (videoFrameQueue.size() > 0) {
            AVFrame* avFrame = getVideoFrame();
            if (avFrame->pts != AV_NOPTS_VALUE){
                double timestamp = avFrame->pts*av_q2d(formatContext->streams[videoStream]->time_base);
                if (timestamp > audio_clock) {
                    usleep((unsigned long)((timestamp - audio_clock)*1000000));
                }
            }
            if (ANativeWindow_lock(pANativeWindow, &windowBuffer, NULL) >= 0){
                sws_scale(pSwsCtx, (const uint8_t *const *) avFrame->data, avFrame->linesize,
                          0, avFrame->height,
                          rgbFrame->data, rgbFrame->linesize);
                uint8_t * firstWindow = (uint8_t *) windowBuffer.bits;
                uint8_t * firstFrame = rgbFrame->data[0];
                int windowLineByte = windowBuffer.stride * 4;
                int frameLineByte = rgbFrame->linesize[0];
                for(int i=0;i<videoContext->height;i++){
                    memcpy(firstWindow+i*windowLineByte,firstFrame+i*frameLineByte,frameLineByte);
                }
                ANativeWindow_unlockAndPost(pANativeWindow);
            }
            av_frame_free(&avFrame);
        }
    }
}

int count=0;
void SimplePlay::playAudio() {

    JNIEnv *env;
    if (javaVM->AttachCurrentThread(&env, NULL) != JNI_OK) {
        LOGE("获取JNIEnv失败");
        return;
    }
    //-----------------初始化音频参数Start---------------//

    uint8_t outChLayout = AV_CH_LAYOUT_STEREO;
    int outChLayoutNb=av_get_channel_layout_nb_channels(outChLayout);
    AVSampleFormat outSampleFmt = AV_SAMPLE_FMT_S16;
    int outSampleRate = audioContext->sample_rate;
    SwrContext *pSwrCtx = swr_alloc();
    swr_alloc_set_opts(pSwrCtx,
                       outChLayout, outSampleFmt, outSampleRate,
                       audioContext->channel_layout, audioContext->sample_fmt, audioContext->sample_rate,
                       0, NULL);
    swr_init(pSwrCtx);
    //-----------------初始化音频参数End---------------//

    //--------------通过反射获取音频播放器Start--------//
    jclass mediaPlay = env->GetObjectClass(instance);
    jmethodID createTrackId = env->GetMethodID(mediaPlay, "createTrack", "(II)V");
    env->CallVoidMethod(instance, createTrackId, outSampleRate, outChLayoutNb);
    jmethodID playTrackId = env->GetMethodID(mediaPlay, "playTrack", "([BI)V");
    //--------------通过反射获取音频播放器End--------//
    uint8_t *outBuffer=(uint8_t *) av_malloc(44100 * 2);

    while (true) {
        if (audioFrameQueue.size() > 0) {
            AVFrame *avFrame = getAudioFrame();
            int nb=swr_convert(pSwrCtx, &outBuffer, 44100 * 2,
                        (const uint8_t **) avFrame->data, avFrame->nb_samples);
            int size = av_samples_get_buffer_size(NULL, outChLayoutNb, avFrame->nb_samples,
                                                  outSampleFmt, 1);

            jbyteArray byteArray = env->NewByteArray(size);
            env->SetByteArrayRegion(byteArray, 0, size, (const jbyte *) outBuffer);
            env->CallVoidMethod(instance, playTrackId, byteArray, size);
            env->DeleteLocalRef(byteArray);

            AVStream * stream = formatContext->streams[audioStream];
            if (avFrame->pts != AV_NOPTS_VALUE){
                audio_clock = (avFrame->pts) * av_q2d(stream->time_base);
                int data_size = avFrame->channels * nb * av_get_bytes_per_sample(outSampleFmt);
                audio_clock += static_cast<double>(data_size) /
                               (2 * stream->codec->channels * stream->codec->sample_rate);
            }
            av_frame_free(&avFrame);
        }
    }
}


